
using System.Linq;
using Godot;

namespace Arcane.Source.UI
{
    [Tool]
    [GlobalClass]
    public partial class FixedGridContainer : Container
    {
        public enum SpacingMode
        {
            Pixel, WidthPercentage, HeightPercentage, AutoPercentage
        }
        private Vector2I _size;
        private double _spacing;
        private double _pixelSpacing;
        private double _cellSize;

        [Export]
        Vector2I RowColumn
        {
            get => _size;
            set
            {
                _size = value;
                OnResize();
            }
        }
        [Export]
        double Spacing
        {
            get => _spacing;
            set
            {
                _spacing = value;
                _pixelSpacing = GetPixelSpacing();
                OnResize();
            }
        }
        [Export] SpacingMode mode;
        private Alignment _align;
        [Export]
        public Alignment Alignment
        {
            get => _align;
            set
            {
                _align = value;
                FitChildren();
            }
        }

        // public override void _Draw()
        // {
        //     var rectSize = GetRect().Size;
        //     DrawRect(new(Vector2.Zero, rectSize), Colors.White);

        //     var s = GetSpacing();
        //     var offsetX = 0.5f * (rectSize.X - size.X * _cellSize - (size.X - 1) * s);
        //     var offsetY = 0.5f * (rectSize.Y - size.Y * _cellSize - (size.Y - 1) * s);
        //     for (int y = 0; y < size.Y; y++)
        //     {
        //         var cellY = y * _cellSize + y * s;
        //         for (int x = 0; x < size.X; x++)
        //         {
        //             var cellX = x * _cellSize + x * s;
        //             DrawRect(new((float)(offsetX + cellX), (float)(offsetY + cellY), new((float)_cellSize, (float)_cellSize)), Colors.Gray);
        //         }
        //     }
        // }

        public double GetPixelSpacing()
        {
            var rectSize = GetRect().Size;
            return mode switch
            {
                SpacingMode.Pixel => _spacing,
                SpacingMode.WidthPercentage => rectSize.X * _spacing,
                SpacingMode.HeightPercentage => rectSize.Y * _spacing,
                SpacingMode.AutoPercentage => (rectSize.X / rectSize.Y) > ((float)_size.X / _size.Y) ? rectSize.Y * _spacing : rectSize.X * _spacing,
                _ => _spacing,
            };
        }

        public override void _Notification(int what)
        {
            if (what == NotificationChildOrderChanged)
            {
                FitChildren();
            }
            else if (what == NotificationReady || what == NotificationResized)
            {
                OnResize();
            }
        }

        private void FitChildren()
        {
            if (_size.X <= 0 || _size.Y <= 0) return;
            var (offsetX, offsetY) = GetOffset();
            var index = 0;
            foreach (var child in GetChildren().Cast<Control>())
            {
                var x = index % _size.X;
                var y = index / _size.X;

                var cellX = x * _cellSize + x * _pixelSpacing;
                var cellY = y * _cellSize + y * _pixelSpacing;
                if (y < _size.Y)
                {
                    child.Visible = true;
                    FitChildInRect(child, new((float)(offsetX + cellX), (float)(offsetY + cellY), new((float)_cellSize, (float)_cellSize)));
                }
                else
                {
                    child.Visible = false;
                }
                index += 1;
            }
        }

        private void OnResize()
        {
            var (width, height) = GetRect().Size;
            var gridWidth = width - _pixelSpacing * (_size.X - 1);
            var gridHeight = height - _pixelSpacing * (_size.Y - 1);

            var aspect = (double)_size.X / _size.Y;
            var gridAspect = (double)gridWidth / gridHeight;

            // grid width is longer than width, so height determines cell size.
            if (gridAspect > aspect)
            {
                _cellSize = gridHeight / _size.Y;
            }
            else // grid height is higher than height, width determines cell size.
            {
                _cellSize = gridWidth / _size.X;
            }
            _pixelSpacing = GetPixelSpacing();
            FitChildren();
        }

        private Vector2 GetOffset() => _align switch
        {
            Alignment.End => new Vector2((float)(GetRect().Size.X - _size.X * _cellSize - (_size.X - 1) * _pixelSpacing), (float)(GetRect().Size.Y - _size.Y * _cellSize - (_size.Y - 1) * _pixelSpacing)),
            Alignment.Center => 0.5f * new Vector2((float)(GetRect().Size.X - _size.X * _cellSize - (_size.X - 1) * _pixelSpacing), (float)(GetRect().Size.Y - _size.Y * _cellSize - (_size.Y - 1) * _pixelSpacing)),
            _ => Vector2.Zero,
        };
    }
}